#include "core\TextureManager.h"
#include "common\Debug.h"
#include <string>
#include <fstream>
#include <Windows.h>
#include <gl\gl.h>	
#include <gl\glu.h>	

TextureManager::TextureManager()
{
}

TextureManager::~TextureManager()
{
}

bool TextureManager::loadTextureFromDisk(const char* file, glTexture* texture)
{
	std::string filename(file);
	std::string extension(filename.substr(1 + filename.find_last_of('.')));

	if(extension.compare("tga") == 0)
	{
		texture->type = TGA;
		return loadTGAFromDisk(file, texture);
	}
	else
	{
		texture->type = UNKNOWN;
		if(extension.compare("bmp") == 0) texture->type = BMP;
		else if(extension.compare("jpg") == 0 || extension.compare("jpeg")) texture->type = JPG;
		else if(extension.compare("gif") == 0) texture->type = GIF;
		else if(extension.compare("ico") == 0) texture->type = ICO;
		else if(extension.compare("EMF") == 0) texture->type = EMF;
		else if(extension.compare("WMF") == 0) texture->type = WMF;
		return loadBMPFromDisk(file, texture);
	}
}

void TextureManager::freeTexture(glTexture* texture)
{
	glDeleteTextures(1, &texture->textureID);
}

bool TextureManager::loadBMPFromDisk(const char *file, glTexture* texture)
{
	HBITMAP hBmp = (HBITMAP) LoadImage(NULL, file, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION);
	if(hBmp == 0)
	{
		debug_printf("could not load BMP file: %s\n",file);
		return false;
	}
	BITMAP bmp;
	GetObject(hBmp, sizeof(bmp), &bmp);
	//glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
	glGenTextures(1, &texture->textureID);
	texture->bpp = bmp.bmBitsPixel;
	texture->glType = GL_BGR_EXT;
	texture->width = bmp.bmWidth;
	texture->height = bmp.bmHeight;
	generateTexture(texture, (char*)bmp.bmBits);
	return true;
}

void TextureManager::generateTexture(glTexture* texture, char *data)
{
	int channels;
	if(texture->glType == GL_RGBA) channels = 4;
	else channels = 3;
	glBindTexture(GL_TEXTURE_2D, texture->textureID);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); 
  unsigned char* uData = new unsigned char[channels* texture->width* texture->height];
  for(int i=0;i<channels* texture->width* texture->height;i++) uData[i] = (data[i]>=0)?data[i]:(data[i]+256);
  gluBuild2DMipmaps(GL_TEXTURE_2D, channels, texture->width, texture->height, texture->glType, GL_UNSIGNED_BYTE, uData);
  delete[] uData;
}

bool TextureManager::loadTGAFromDisk(const char* file,glTexture* texture)
{
	tgaHeader header;
	std::ifstream fin;
	fin.open(file, std::fstream::in | std::fstream::binary);
	if(!fin.is_open())
	{
		debug_printf("could not load file: %s\n",file);
		return false;
	}
	fin.read((char*)&header, sizeof(tgaHeader));
	if(!fin.good())
	{
		debug_printf("could not load TGA header in file: %s\n",file);
		fin.close();
		return false;
	}

	int imageType = header.imgType;
	int imageWidth = header.widthLo + header.widthHi * 256;
	int imageHeight = header.heightLo + header.heightHi * 256;
	int imageBytesPerPixel = header.bpp / 8;
	int imageSize = imageWidth * imageHeight * imageBytesPerPixel;
	texture->width = imageWidth;
	texture->height = imageHeight;
	texture->bpp = header.bpp;  
	if(header.bpp == 24) texture->glType = GL_RGB;
	else texture->glType = GL_RGBA;

	if((imageType != 2 && imageType != 10) || imageWidth == 0 || imageHeight == 0 || (imageBytesPerPixel != 3 && imageBytesPerPixel != 4))
	{
		debug_printf("invalid TGA header in file: %s\n",file);
		fin.close();
		return false;
	}

	char* imageData = new char[imageSize];

	if(header.imgIdent > 0) fin.seekg(header.imgIdent, std::ios::cur);

	// uncompressed tga
	if(imageType == 2)
	{
		fin.read(imageData, imageSize);
		if(fin.gcount() != imageSize)
		{
			delete[] imageData;
			debug_printf("could not load TGA data in file: %s\n",file);
			fin.close();
			return false;
		}
		for(int i=0;i<imageSize;i+=imageBytesPerPixel)
		{
			std::swap(imageData[i], imageData[i+2]);
		}
	}
	// compressed tga
	else
	{
		delete[] imageData;
		debug_printf("loading of compressed TGA data not implemented, file: %s\n",file);
		fin.close();
		return false;
	}

	fin.close();
  
	int maxTexSize;
	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize);
	int width;
	int height;
	if(imageWidth <= maxTexSize) width = 1 << (int)floor((log((float)imageWidth) / log(2.f)) + 0.5f);
	else width = maxTexSize;
	if(imageHeight <= maxTexSize) height = 1 << (int)floor((log((float)imageHeight) / log(2.f)) + 0.5f);
	else height = maxTexSize;
	if(width != imageWidth || height != imageHeight)
	{
		char* imageDataResized = new char[width * height * imageBytesPerPixel];
		if(imageBytesPerPixel == 4) gluScaleImage(GL_RGBA, imageWidth, imageHeight, GL_BYTE, imageData, width, height, GL_BYTE, imageDataResized);
		else gluScaleImage(GL_RGB, imageWidth, imageHeight, GL_BYTE, imageData, width, height, GL_BYTE, imageDataResized);
		delete[] imageData;
		imageData = imageDataResized;
		texture->width = width;
		texture->height = height;
	}
  
	glGenTextures(1, &texture->textureID);
	generateTexture(texture, imageData);
	delete[] imageData;

	return true;
}